矩阵对三维模型进行旋转拉伸变换的交互实现(python)¶

前言:¶

在本文中,我们将使用python语言编写一个程序,使用矩阵对三维模型进行交互式的旋转拉伸变换。

In [138]:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from ipywidgets import interact ,FloatSlider,Dropdown,Select,ToggleButtons

1.定义三维模型数据生成函数¶

In [139]:
def sphere():
    "generate the coordarate of a unit sphere"
    u = np.linspace(-np.pi, np.pi, 100)
    v = np.linspace(0, np.pi, 100)
    x = np.outer(np.cos(u), np.sin(v))
    y = np.outer(np.sin(u), np.sin(v))
    z = np.outer(np.ones(np.size(u)), np.cos(v))
    return x,y,z
In [140]:
def cube():
    dx,dy,dz=1,1,1
    x_start,y_start,z_start=0,0,0
    
    x=np.linspace( x_start-dx/2, x_start+dx/2,2)
    y=np.linspace( y_start-dy/2,y_start+dy/2,2)
    z=np.linspace( z_start-dz/2, z_start+dz/2,2)
    #生成六个面的坐标数据
    zx,zy=np.meshgrid(x,y)
    xz,xy=np.meshgrid(z,y)
    yz,yx=np.meshgrid(z,x)
     
    planeD=np.array([[np.full_like(xz,x_start+dx/2),xy, xz],
            [np.full_like(xz,x_start-dx/2),xy, xz],
            [yx, np.full_like(yz,y_start+dy/2),yz],
            [yx, np.full_like(yz,y_start-dy/2),yz],
            [zx, zy, np.full_like(zx,z_start+dz/2)],
           [zx, zy, np.full_like(zx,z_start-dz/2)]])
    
    return planeD   
In [141]:
def parabolic_cylinder():
    """柱形抛面"""
    x=np.linspace(-1,1,100)
    y=np.linspace(-1,1,100)
    
    xx,yy=np.meshgrid(x,y)
    zz=xx**2
    return xx,yy,zz
In [142]:
def parabolic():
    """抛物面"""
    x=np.linspace(-1,1,100)
    y=np.linspace(-1,1,100)
    
    xx,yy=np.meshgrid(x,y)
    zz=np.sqrt(xx**2+yy**2)
    return xx,yy,zz

2.定义变换矩阵生成函数¶

In [143]:
def MatrixGenerator(x,y,z,theta_x, theta_y, theta_z):
    "description:"
    "create a matrix using to rotate and strech"
    "Parameter"
    "x,y,z:how many times strech along the initial x , y,z axis"
    "theta_x, theta_y, theta_z:the angle rotated around x ,y ,z axis"
    theta = np.array([theta_x, theta_y, theta_z])
    Sigma = np.diag([x, y, z]) # scale x, then y, then z

    # Rotation about x axis
    Rx = np.array([[1, 0, 0],
                   [0, np.cos(theta[0]), -np.sin(theta[0])],
                   [0, np.sin(theta[0]), np.cos(theta[0])]])

    # Rotation about y axis
    Ry = np.array([[np.cos(theta[1]), 0, np.sin(theta[1])],
                   [0, 1, 0],
                   [-np.sin(theta[1]), 0, np.cos(theta[1])]])

    # Rotation about z axis
    Rz = np.array([[np.cos(theta[2]), -np.sin(theta[2]), 0],
                   [np.sin(theta[2]), np.cos(theta[2]), 0],
                   [0, 0, 1]])
    
    # Rotate and scal
    return  Rz @ Ry @ Rx @ Sigma

3. 定义变换矩阵作用在三维模型数据算法函数¶

In [172]:
def rot(xx,yy,zz,A):
     """
     description:
     使用矩阵A对三维坐标xx,yy,zz进行变换
     
     parameter:
     xx,yy,zz :模型的x y z三维坐标
     A:对模型进行线性变换的矩阵
     """
    
     xR = np.zeros_like(xx)
     yR = np.zeros_like(yy)
     zR = np.zeros_like(zz)
     for i in range(xx.shape[0]):
        for j in range(xx.shape[1]):
            vec = [xx[i,j], yy[i,j], zz[i,j]]
            vecR = A @ vec
            xR[i,j] = vecR[0]
            yR[i,j] = vecR[1]
            zR[i,j] = vecR[2]

     return xR,yR,zR

4.将以上三个函数组合起来构建最终的变换函数¶

In [173]:
def transform(shape,xs,ys,zs,theta_x, theta_y, theta_z):
    # Plot the module
    fig = plt.figure()
    ax1 = fig.add_subplot(121, projection='3d')
    ax2 = fig.add_subplot(122, projection='3d')
    X=MatrixGenerator(xs,ys,zs,theta_x, theta_y, theta_z)
    
    if shape==cube:      
        planeD=cube()    
        
        for i in range(6):
            surf1xu = ax1.plot_surface(planeD[i,0],planeD[i,1],planeD[i,2])
            surf1xu.set_edgecolor('k')
        ax1.set_xlim3d(-2, 2)
        ax1.set_ylim3d(-2, 2)
        ax1.set_zlim3d(-2,2)
        ax1.set(xlabel="x",ylabel="y",zlabel="z",title="原始三维模型")  
        
        for i in range(6):
            xR,yR,zR=rot(planeD[i,0],planeD[i,1,],planeD[i,2],X)
            surf2xu = ax2.plot_surface(xR, yR, zR)
            surf2xu.set_edgecolor('k')
        ax2.set_xlim3d(-2, 2)
        ax2.set_ylim3d(-2, 2)
        ax2.set_zlim3d(-2,2)
        ax2.set(xlabel="x",ylabel="y",zlabel="z",title="变换后三维模型")  
 

    else:
        x ,y ,z=shape()
        surf1 = ax1.plot_surface(x, y, z, cmap='jet',alpha=0.6,facecolors=plt.cm.jet(z),linewidth=0.5,rcount=30,ccount=30)
        surf1.set_edgecolor('k')
        ax1.set_xlim3d(-2, 2)
        ax1.set_ylim3d(-2, 2)
        ax1.set_zlim3d(-2, 2)

        # plot the tranformed module

        xR,yR,zR=rot(x,y,z,X)
        ax2 = fig.add_subplot(122, projection='3d')
        surf2 = ax2.plot_surface(xR, yR, zR,cmap='jet',alpha=0.6,linewidth=0.5,facecolors=plt.cm.jet(z),rcount=30,ccount=30)
        surf2.set_edgecolor('k')
        ax2.set_xlim3d(-2, 2)
        ax2.set_ylim3d(-2, 2)
        ax2.set_zlim3d(-2,2)

5.矩阵对三维模型变换的交互实现¶

In [174]:
%matplotlib inline
plt.rcParams['figure.figsize'] = [10, 5]
plt.rcParams.update({'font.size': 18})
interact(transform,shape=ToggleButtons(options=[('sphere',sphere), ('cube',cube),('parabolic',parabolic),('parabolic_cylinder',parabolic_cylinder)],value=sphere,description='3D module',disabled=False,),
                     xs=FloatSlider(min=0,max=2,step=0.1,value=1,description='x方向伸缩',continuous_update=False),
                     ys=FloatSlider(min=0,max=2,step=0.1,value=1,description='y方向伸缩',continuous_update=False),
                     zs=FloatSlider(min=0,max=2,step=0.1,value=1,description='z方向伸缩',continuous_update=False),
                    theta_x=FloatSlider(min=0,max=2*np.pi,step=0.1,value=1,description='绕x轴旋转',continuous_update=False), 
                     theta_y=FloatSlider(min=0,max=2*np.pi,step=0.1,value=1,description='绕y轴旋转',continuous_update=False),
                     theta_z=FloatSlider(min=0,max=2*np.pi,step=0.1,value=1,description='绕z轴旋转',continuous_update=False))
interactive(children=(ToggleButtons(description='3D module', options=(('sphere', <function sphere at 0x0000022…
Out[174]:
<function __main__.transform(shape, xs, ys, zs, theta_x, theta_y, theta_z)>
In [ ]: